/*
 * @author Stanislovas Mickus
 * @version 5
 * @date 17 July, 2013
 *
 *  Copyright (C) 2013 Stanislovas Mickus
 *
 * Licensed under the GNU General Public License, Version 3 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.gnu.org/copyleft/gpl.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.stasmobstudios.musicplayer.activities;

import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import com.stasmobstudios.musicplayer.adapters.MusicLibAdapter;
import com.stasmobstudios.musicplayer.adapters.MusicViewPagerAdapter;
import com.stasmobstudios.musicplayer.interfaces.PlaybackServiceInterface;
import com.stasmobstudios.musicplayer.interfaces.UIUpdateCallbacks;
import com.stasmobstudios.musicplayer.views.ClockSeekBar;
import com.stasmobstudios.musicplayer.R;
import com.stasmobstudios.musicplayer.broadcasts.RemoteControlReceiver;
import com.stasmobstudios.musicplayer.views.NotificationView;
import com.stasmobstudios.musicplayer.views.RotaryKnobView;
import com.stasmobstudios.musicplayer.viewgroups.StopSwipeViewPager;
import com.stasmobstudios.musicplayer.services.PlaybackService;
import com.stasmobstudios.musicplayer.util.MusicUtils;

import net.hockeyapp.android.CrashManager;
import net.hockeyapp.android.UpdateManager;

import java.io.ByteArrayInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class MusicPlayerUI extends FragmentActivity  implements UIUpdateCallbacks {
    private MusicViewPagerAdapter mMusicViewPagerAdapter;
    private StopSwipeViewPager mViewPager;
    private ComponentName mRemoteControlResponder;
    private AudioManager mAudioManager;
    private static Method mRegisterMediaButtonEventReceiver;
    private static Method mUnregisterMediaButtonEventReceiver;
    private static MusicPlayerUI MUSIC_PLAYER_UI;
    private NotificationView mNotificationWidget;
    private ArrayList<OnConnectedToPlaybackServiceListener> mPlaybackServiceConectedListeners;
    private OnLibraryCursorLoadFinishedListener mLibraryLoadedListener;
    private boolean mArtworkSet = false;
    private String TAG;
    private String APP_ID = "2d782a7816e5d77756f998f3013efa89";

    static {
        // Register for remote controller
        initializeRemoteControlRegistrationMethods();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TAG = this.getClass().getSimpleName().toUpperCase();
        MUSIC_PLAYER_UI = this;

        setContentView(R.layout.activity_music_player_ui);

        mPlaybackServiceConectedListeners = new ArrayList<OnConnectedToPlaybackServiceListener>();

        // Set up the ViewPager with the sections adapter.
        mMusicViewPagerAdapter = new MusicViewPagerAdapter(getSupportFragmentManager(), this);
        mViewPager = (StopSwipeViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mMusicViewPagerAdapter);
        mViewPager.setOffscreenPageLimit(2);

        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        setVolumeControlStream(AudioManager.STREAM_MUSIC);
        mRemoteControlResponder = new ComponentName(getPackageName(), RemoteControlReceiver.class.getName());

        if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
           getActionBar().hide();
        }

        //Register receiver
        doBindService();

        checkForUpdates();
    }

    public static MusicPlayerUI getMusicPlayerActivity() {
        return MUSIC_PLAYER_UI;
    }

    /**
    **************************  PlaybackService handling methods **************************
    */
    private PlaybackServiceInterface mBoundService;
    private boolean mIsBound = false;

    private ServiceConnection mConnection = new ServiceConnection() {
        private String CLASS_NAME = this.getClass().getSimpleName().toUpperCase();

        // Do initialization after we connect with playback service
        public void onServiceConnected(ComponentName className, IBinder service) {
            mBoundService = ((PlaybackService.LocalBinder) service).getService();
            mBoundService.initControlUICallbacks(MusicPlayerUI.this);

            // Notification control initialization
            mNotificationWidget = new NotificationView(MusicPlayerUI.this, getPackageName());
            mNotificationWidget.initNotification(getText(R.string.playback_service_started));

            // Notify connected to PlaybackService
            if (mPlaybackServiceConectedListeners.size() > 0)
                for (int i = 0; i < mPlaybackServiceConectedListeners.size(); i++) {
                    mPlaybackServiceConectedListeners.get(i).onConnected();
                }

            // Register for remote controller
            registerRemoteControl();
        }

        // Do cleaning after disconnecting from playback server
        public void onServiceDisconnected(ComponentName className) {
            mBoundService = null;
        }
    };

    /**
     * Connect to playback service
     */
    void doBindService() {
        Intent startPlaybackServiceIntent = new Intent(MusicPlayerUI.this, PlaybackService.class);
        startService(startPlaybackServiceIntent);
        bindService(startPlaybackServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }

    /**
     * Disconnect from playback service
     */
    void doUnbindService() {
        if (mIsBound) {
            // Detach our existing connection.
            unbindService(mConnection);
            mIsBound = false;
            if (mBoundService != null)
                ((Service) mBoundService).stopSelf();
        }
    }

    // Get music playback service
    public PlaybackServiceInterface getPlaybackSrevice() {
        return mBoundService;
    }

    // Get music player UI view pager
    public StopSwipeViewPager getmMusicPlayerUIViewPager() {
        return mViewPager;
    }


    /**
     **************************  Remote control methods   **************************
     */

    /**
     * Initialize remote control methods
     */
    private static void initializeRemoteControlRegistrationMethods() {
        try {
            if (mRegisterMediaButtonEventReceiver == null) {
                mRegisterMediaButtonEventReceiver = AudioManager.class.getMethod(
                        "registerMediaButtonEventReceiver",
                        new Class[] { ComponentName.class } );
            }
            if (mUnregisterMediaButtonEventReceiver == null) {
                mUnregisterMediaButtonEventReceiver = AudioManager.class.getMethod(
                        "unregisterMediaButtonEventReceiver",
                        new Class[] { ComponentName.class } );
            }
            //success, this device will take advantage of better remote
            //control event handling
        } catch (NoSuchMethodException nsme) {
            Log.e("MusicPlayerUI", "initializeRemoteControlRegistrationMethods(): FAILED to initialize remote control methods: " + nsme);
        }
    }

    /**
     * Register for remote controller
     */
    private void registerRemoteControl() {
        try {
            if (mRegisterMediaButtonEventReceiver == null) {
                return;
            }
            mRegisterMediaButtonEventReceiver.invoke(mAudioManager, mRemoteControlResponder);
        } catch (InvocationTargetException ite) {
            //unpack original exception when possible
            Throwable cause = ite.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            } else if (cause instanceof Error) {
                throw (Error) cause;
            } else {
                //unexpected checked exception; wrap and re-throw
                throw new RuntimeException(ite);
            }
        } catch (IllegalAccessException ie) {
            Log.e(TAG, "registerRemoteControl(): UNEXPECTED " + ie);
        }
    }

    /**
     * Unregister for remote controller
     */
    private void unregisterRemoteControl() {
        try {
            if (mUnregisterMediaButtonEventReceiver == null) {
                return;
            }
            mUnregisterMediaButtonEventReceiver.invoke(mAudioManager, mRemoteControlResponder);
        } catch (InvocationTargetException ite) {
            //unpack original exception when possible
            Throwable cause = ite.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            } else if (cause instanceof Error) {
                throw (Error) cause;
            } else {
                //unexpected checked exception; wrap and re-throw
                throw new RuntimeException(ite);
            }
        } catch (IllegalAccessException ie) {
            Log.e(TAG, "unregisterRemoteControl(): UNEXPECTED " + ie);
        }
    }


    /**
     **************************  Media player UI update methods   **************************
     */

    /**
     * Update volume display on UI
     */
    public void refreshVolume() {
        TextView volLabel = (TextView) findViewById(R.id.volLabel);
        RotaryKnobView volumeKnob = (RotaryKnobView) findViewById(R.id.volumeKnob);

        AudioManager audioManager = (AudioManager) getBaseContext().getSystemService(Context.AUDIO_SERVICE);
        int streamVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        int streamMaxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        int streamVolProc = (streamVolume * 100) / streamMaxVolume;

        volumeKnob.setValue(streamVolProc);
        volLabel.setText(String.valueOf(streamVolProc));
    }

    /**
     * Change current playing track artwork
     */
    public void changeAlbumArtwork() {
        View mplayerView = findViewById(R.id.main_view);

        byte[] bytesAlbumartwork = mBoundService.getAlbumArtwork();
        if (bytesAlbumartwork != null) {
            mplayerView.setBackgroundDrawable(Drawable.createFromStream(new ByteArrayInputStream(bytesAlbumartwork), "Album Artwork"));
            mNotificationWidget.updateAlbumArtwork(BitmapFactory.decodeByteArray(bytesAlbumartwork, 0, bytesAlbumartwork.length));
            mArtworkSet = true;
        } else if (mArtworkSet && bytesAlbumartwork == null) {
            mplayerView.setBackgroundResource(R.color.black);
            mNotificationWidget.updateAlbumArtwork(null);
            mArtworkSet = false;
        }
    }

    /**
     * Update current playing track info
     */
    public void updateCurrentTrackInfo () {
        String artistName = mBoundService.getTrackArtist();
        String albumName = mBoundService.getTrackAlbum();
        String trackName = mBoundService.getTrackName();

        // Update Track view
        ((TextView) findViewById(R.id.labelArtist)).setText(artistName);
        ((TextView) findViewById(R.id.labelAlbumName)).setText(albumName);
        ((TextView) findViewById(R.id.labelTrackName)).setText(trackName);
        ((TextView) findViewById(R.id.trackTime)).setText(MusicUtils.makeTimeString(this, mBoundService.getTrackCurrentTime() / 1000));
        ((TextView) findViewById(R.id.trackTotalTime)).setText(MusicUtils.makeTimeString(this, mBoundService.getTrackDuration() / 1000));
        //Update artwork
        changeAlbumArtwork();
        // Update playback control notification
        mNotificationWidget.updateTrackInfo(trackName, albumName, artistName);
    }

    public void updatePlayingSate(boolean isPlaying) {
        if (isPlaying) {
            ((Button) findViewById(R.id.first_flow)).setBackgroundResource(R.drawable.playpause);
            mNotificationWidget.updatePlayPause(true);
        } else {
            ((Button) findViewById(R.id.first_flow)).setBackgroundResource(R.drawable.play);
            mNotificationWidget.updatePlayPause(false);
        }
    }

    /**
     * Update current playing / selected track info
     *
     * @param currentPosition Give track ID to select
     */
    public void updateCurrentTrackSelection(int currentPosition) {
        ListView trackListView = (ListView) findViewById(R.id.track_list);
        int currentVisibleTrack = trackListView.getFirstVisiblePosition();
        View v = trackListView.getChildAt(0);
        int top = (v == null) ? 0 : v.getTop();
        ((MusicLibAdapter) trackListView.getAdapter()).setCurrentTrack(currentPosition);
        trackListView.setAdapter((MusicLibAdapter) trackListView.getAdapter());
        trackListView.setSelectionFromTop(currentVisibleTrack, top);
    }

    /**
     **************************  Listeners   **************************
     */

    public interface OnConnectedToPlaybackServiceListener {
        public void onConnected();
    }

    public interface OnLibraryCursorLoadFinishedListener {
        public void onLoadFinished();
    }

    public void setOnConectedToPlaybackServiceListener(OnConnectedToPlaybackServiceListener onConnectedToPlaybackServiceListener) {
        mPlaybackServiceConectedListeners.add(onConnectedToPlaybackServiceListener);
    }

    public void setOnLibraryCursorLoadFinishedListener(OnLibraryCursorLoadFinishedListener onLibraryCursorLoadFinishedListener) {
        mLibraryLoadedListener = onLibraryCursorLoadFinishedListener;
    }

    // Notify lib cursor finished loading
    public void notifyLibCursorLoadFinished() {
        if (mLibraryLoadedListener != null)
            mLibraryLoadedListener.onLoadFinished();
    }

    /**
     * Listen for key press
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
        if (KeyEvent.KEYCODE_VOLUME_UP == keyEvent.getKeyCode() || KeyEvent.KEYCODE_VOLUME_DOWN == keyEvent.getKeyCode() || KeyEvent.KEYCODE_VOLUME_MUTE == keyEvent.getKeyCode()) {
            refreshVolume();
        }
        return super.onKeyDown(keyCode, keyEvent);
    }

    /**
     * On back press put activity to backstack
     */
    @Override
    public void onBackPressed() {
        moveTaskToBack(true);
    }

    /**
     **************************  Playback action methods   **************************
     */

    /**
     * Play pause track action
     *
     * @param button Button clicked
     */
    public void actionPlayPause(View button) {
        if (mBoundService != null)
            updatePlayingSate(mBoundService.mediaPlayPause());
    }

    /**
     * Play previous track action
     *
     * @param button Button clicked
     */
    public void actionPrev(View button) {
        if (mBoundService != null) {
            mBoundService.mediaPrev();
            ((ClockSeekBar) findViewById(R.id.seekCTime)).setProgress(0);
        }
    }

    /**
     * Play next track action
     *
     * @param button Button clicked
     */
    public void actionNext(View button) {
        if (mBoundService != null) {
            mBoundService.mediaNext();
            ((ClockSeekBar) findViewById(R.id.seekCTime)).setProgress(0);
        }
    }

    /**
     **************************  HoeckeyApp methods   **************************
     */
    private void checkForCrashes() {
        CrashManager.register(this, APP_ID);
    }

    private void checkForUpdates() {
        // Remove this for store / production builds!
        UpdateManager.register(this, APP_ID);
    }

    /**
     **************************  Lifecycle methods   **************************
     */

    @Override
    public void onDestroy() {
        // Cancel the persistent notification.
        mNotificationWidget.cancelNotification();

        unregisterRemoteControl();
        doUnbindService();

        super.onDestroy();
    }

    @Override
    protected void onPause() {
        super.onPause();
        UpdateManager.unregister();
    }

    @Override
    protected void onResume() {
        super.onResume();
        checkForCrashes();
    }
}
